﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using NLog.Fluent;

namespace Labthink.PCSoft.UniversalHost4.DataAccess
{
    public class FileSend
    {
        public event EventHandler<FileEndArgs> OnFileSendEnd;

        public event EventHandler<FileStartArgs> OnFileSendStart;

        public event EventHandler<FileTransferArgs> OnFileSendTransfer;

        public int Send(Socket c, FileInfo fi)
        {
            if (string.IsNullOrEmpty(fi.FullName))
            {
                Log.Error().Message("没有找到文件{0}", fi.FullName).Write();
                throw new Exception("file not found");
            }

            if (fi.PackSize <= 0) fi.PackSize = 1024;
            var buffer = new byte[1024];
            var key = Guid.NewGuid().ToString();
            try
            {
                using (var reader = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read, FileShare.None))
                {
                    fi.FileSize = reader.Length;
                    fi.PackCount = (long)Math.Ceiling(fi.FileSize / (double)fi.PackSize);
                    fi.LastPackSize = (int)(fi.FileSize % fi.PackSize);
                    if (fi.LastPackSize == 0)
                        fi.LastPackSize = fi.PackSize;

                    c.Send(Encoding.Unicode.GetBytes(JsonConvert.SerializeObject(fi)));
                    var count = c.Receive(buffer);
                    string mes = Encoding.UTF8.GetString(buffer, 0, count);
                    if (mes != "OK") return -1;

                    _fileSendStart(c, new FileStartArgs
                    {
                        Key = key,
                        FileName = fi.FileName,
                        FileSavePath = fi.FullName,
                        FileSize = fi.FileSize,
                        // ReSharper disable once PossibleNullReferenceException
                        RemoteIp = (c.RemoteEndPoint as IPEndPoint).Address.ToString(),
                        StartTime = DateTime.Now
                    });

                    var tc = 0;
                    buffer = new byte[fi.PackSize];
                    var result = new byte[1024];
                    for (int i = 0; i < fi.PackCount; i++)
                    {
                        //从文件流读取数据并填充数据包   
                        var rc = reader.Read(buffer, 0, buffer.Length);
                        tc += rc;
                        //if (rc != fi.PackSize)
                        //{
                        //    Logger.Debug(string.Format("包序号：{0}，接受字节数：{1}", i, rc));
                        //}
                        //发送数据包   
                        c.Send(buffer, 0, rc, SocketFlags.None);
                        count = c.Receive(result);
                        mes = Encoding.Unicode.GetString(result, 0, count);
                        while (mes == "999")
                        {
                            count = c.Receive(buffer);
                            mes = Encoding.Unicode.GetString(result, 0, count);
                            Log.Debug().Message($"重发第{i}个数据包").Write();
                        }

                        if (i % 100 == 0)
                        {
                            _fileSending(c, new FileTransferArgs
                            {
                                Key = key,
                                LastFileSize = fi.FileSize - i * fi.PackSize,
                                Process = (double)i * fi.PackSize / fi.FileSize,
                                RevicingTime = DateTime.Now
                            });
                            //显示发送数据包的个数   
                            Log.Debug().Message($"发送{i}个文件包").Write();
                        }
                    }

                    Log.Debug().Message($"发送字节数{tc}").Write();
                    count = c.Receive(buffer);
                    mes = Encoding.Unicode.GetString(buffer, 0, count);
                    var success = true;
                    if (mes == "0")
                    {
                        Log.Debug().Message("文件发送成功").Write();
                    }
                    else
                    {
                        success = false;
                        Log.Debug().Message("文件发送失败").Write();
                    }
                    _fileSendEnd(c, new FileEndArgs
                    {
                        Key = key,
                        IsSuccess = success,
                        EndTime = DateTime.Now
                    });
                    c.Close();
                    return success ? 0 : -1;
                }
            }
            catch (Exception ex)
            {
                _fileSendEnd(c, new FileEndArgs
                {
                    Key = key,
                    IsSuccess = false,
                    EndTime = DateTime.Now
                });
                Log.Error().Exception(ex).Message("发送文件{0}失败",  fi.FullName).Write();
                return -1;
            }
        }

        private void _fileSendStart(object sender, FileStartArgs args)
        {
            Log.Debug().Message("调用文件开始接收事件").Write();
            OnFileSendStart?.Invoke(sender, args);
        }

        private void _fileSending(object sender, FileTransferArgs args)
        {
            Log.Debug().Message("调用文件接收中事件").Write();
            OnFileSendTransfer?.Invoke(sender, args);
        }

        private void _fileSendEnd(object sender, FileEndArgs args)
        {
            Log.Debug().Message("调用文件接收完成事件").Write();
            OnFileSendEnd?.Invoke(sender, args);
        }
    }

    public class FileEndArgs : EventArgs
    {
        public string Key { get; set; }
        public bool IsSuccess { get; set; }
        public DateTime EndTime { get; set; }
    }


    public class FileStartArgs : EventArgs
    {
        public string Key { get; set; }
        public string FileName { get; set; }
        public string FileSavePath { get; set; }
        public long FileSize { get; set; }
        public string RemoteIp { get; set; }
        public DateTime StartTime { get; set; }
    }


    public class FileTransferArgs : EventArgs
    {
        public string Key { get; set; }

        public long LastFileSize { get; set; }
        public double Process { get; set; }
        public DateTime RevicingTime { get; set; }
    }
}
